﻿namespace NetFx.MSIL
{
  using System;
  using System.Text;
  using System.Collections;

  using System.Reflection;
  using System.Reflection.Emit;
  using System.Resources;

  using System.Diagnostics;
  using System.Diagnostics.CodeAnalysis;
  using System.Diagnostics.SymbolStore;
  using System.Runtime.InteropServices;

  public class ModuleGenerator:AnyGenerator, TypeContainer
  {
    #region Statics
    public static ModuleGenerator of(ModuleBuilder builder)
    {
      return new ModuleGenerator(builder);
    }
    #endregion
    #region Fields
    protected ModuleBuilder builder;
    protected MethodInfo entrypoint;
    protected ArrayList plainMethods = new ArrayList();
    protected ArrayList types = new ArrayList();
    protected ArrayList fields = new ArrayList();
    #endregion
    #region Properties
    public override object AnyBuilder
    {
      get { return this.builder; }
    }
    public override DefType GeneratorType
    {
      get
      {
        return DefType.Module;
      }
    }
    public virtual ModuleBuilder Builder
    {
      get
      {
        return this.builder;
      }
      set
      {
        if (value == null) throw new ArgumentNullException("builder can not be null!");
        this.builder = value;
      }
    }
    public virtual Assembly Assembly
    {
      get
      {
        return this.Builder.Assembly;
      }
    }
    public virtual string ModuleName
    {
      get
      {
        return this.Builder.Name;
      }
    }
    public virtual string FullQualifiedName
    {
      get
      {
        return this.Builder.FullyQualifiedName;
      }
    }
    public virtual bool IsTransient
    {
      get
      {
        return this.Builder.IsTransient();
      }
    }
    public virtual IList Methods
    {
      get
      {
        return this.plainMethods;
      }
    }
    public virtual IList Types
    {
      get
      {
        return this.types;
      }
    }
    public virtual IList Fields
    {
      get
      {
        return this.fields;
      }
    }
    public virtual ISymbolWriter SymbolWriter
    {
      get
      {
        return this.Builder.GetSymWriter();
      }
    }
    public virtual MethodInfo EntryPoint
    {
      get
      {
        return this.entrypoint;
      }
      set
      {
        this.builder.SetUserEntryPoint(this.entrypoint = value);
      }
    }
    #endregion
    #region Constructors
    public ModuleGenerator(ModuleBuilder builder)
    {
      this.Builder = builder;
    }
    #endregion
    #region Methods
    public virtual void CommitPlainMethods()
    {
      if (this.plainMethods.Count > 0 && !IsCommitted)
      {
        this.Builder.CreateGlobalFunctions();
      }
    }
    public virtual void CommitTypes()
    {
      if (this.Types.Count > 0)
      {
        foreach (TypeGenerator type in this.Types)
        {
          type.CommitGeneration();
        }
      }
    }
    public override void CommitGeneration()
    {
      if (!this.IsCommitted)
      {
        this.CommitTypes();
        this.CommitPlainMethods();
        base.CommitGeneration();
      }
    }
    #region Defines
    public virtual ISymbolDocumentWriter DefineDocument(string url, Guid language, Guid languageVendor, Guid documentType)
    {
      return this.Builder.DefineDocument(url, language, languageVendor, documentType);
    }
    public virtual IResourceWriter DefineResource(string name, string description)
    {
      return this.Builder.DefineResource(name, description);
    }
    public virtual IResourceWriter DefineResource(string name, string description,ResourceAttributes attribute)
    {
      return this.Builder.DefineResource(
        name,
        description,
        attribute
        );
    }
    public virtual void DefineUnmanagedResource(byte[] resource)
    {
      this.Builder.DefineUnmanagedResource(resource);
    }
    public virtual void DefineUnmanagedResource(string resourceFileName)
    {
      this.Builder.DefineUnmanagedResource(resourceFileName);
    }

    public virtual MethodGenerator DefinePlainMethod(string name, MethodAttributes attributes, Type returnType, Type[] parameterTypes)
    {
      return
        this.AddMethod(
          this.WrapDefinePlainMethod(
          name,
          attributes,
          returnType,
          parameterTypes
          )
        );
    }
    public virtual MethodGenerator DefinePlainMethod(string name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes)
    {
      return
        this.AddMethod(
          this.WrapDefinePlainMethod(
          name,
          attributes,
          callingConvention,
          returnType,
          parameterTypes
          )
        );
    }
    public virtual MethodGenerator DefinePInvokeMethod(string name, string dllName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet)
    {
      return
        this.AddMethod(
          this.WrapDefinePInvokeMethod(
          name,
          dllName,
          attributes,
          callingConvention,
          returnType,
          parameterTypes,
          nativeCallConv,
          nativeCharSet
          )
        );
    }
    public virtual MethodGenerator DefinePInvokeMethod(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet)
    {
      return
        this.AddMethod(
          this.WrapDefinePInvokeMethod(
          name,
          dllName,
          entryName,
          attributes,
          callingConvention,
          returnType,
          parameterTypes,
          nativeCallConv,
          nativeCharSet
          )
        );

    }

    public virtual EnumGenerator DefineEnumeration(string name, TypeAttributes attributes, Type underlingType)
    {
      return this.AddType(this.WrapDefineEnum(name,attributes,underlingType)) as EnumGenerator;
    }
    public virtual TypeGenerator DefineType(string name)
    {
      return this.AddType(this.WrapDefineType(name));
    }
    public virtual TypeGenerator DefineType(string name, TypeAttributes attr)
    {
      return this.AddType(this.WrapDefineType(name,attr));
    }
    public virtual TypeGenerator DefineType(string name, TypeAttributes attr, Type parent)
    {
      return this.AddType(this.WrapDefineType(name,attr,parent));
    }
    public virtual TypeGenerator DefineType(string name, TypeAttributes attr, Type parent, PackingSize packSize)
    {
      return this.AddType(this.WrapDefineType(name,attr,parent,packSize));
    }
    public virtual TypeGenerator DefineType(string name, TypeAttributes attr, Type parent, Type[] interfaces)
    {
      return this.AddType(this.WrapDefineType(name,attr,parent,interfaces));
    }

    public virtual FieldGenerator DefineField(string name, byte[] data, FieldAttributes attributes)
    {
      return this.AddField(this.WrapDefineInitializedData(name, data, attributes));
    }
    public virtual FieldGenerator DefineField(string name, int size, FieldAttributes attributes)
    {
      return this.AddField(this.WrapDefineUninitializedData(name, size, attributes));
    }
    #region GetTypes
    public virtual Type GetType(string name)
    {
      return this.Builder.GetType(name);
    }
    public virtual Type GetType(string name,bool ignoreCase)
    {
      return this.Builder.GetType(name,ignoreCase);
    }
    public virtual Type GetType(string name,bool throwOnError,bool ignoreCase)
    {
      return this.Builder.GetType(name,throwOnError,ignoreCase);
    }
    #endregion
    #region GetTokens
    public virtual SignatureToken GetSignatureToken(SignatureHelper sigHelper)
    {
      return this.Builder.GetSignatureToken(sigHelper);
    }
    public virtual SignatureToken GetSignatureToken(byte[] sigBytes, int count)
    {
      return this.Builder.GetSignatureToken(sigBytes, count);
    }
    public virtual StringToken GetStringToken(string s)
    {
      return this.Builder.GetStringConstant(s);
    }
    public virtual FieldToken GetFieldToken(FieldInfo field)
    {
      return this.Builder.GetFieldToken(field);
    }
    public virtual MethodToken GetMethodToken(MethodInfo method)
    {
      return this.Builder.GetMethodToken(method);
    }
    public virtual MethodToken GetConstructorToken(ConstructorInfo con)
    {
      return this.Builder.GetConstructorToken(con);
    }
    public virtual TypeToken GetTypeToken(string name)
    {
      return this.Builder.GetTypeToken(name);
    }
    public virtual TypeToken GetTypeToken(Type type)
    {
      return this.Builder.GetTypeToken(type);
    }
    #endregion
    #region Symbol
    public virtual void SetSymbolAttribute(string name, byte[] data)
    {
      this.Builder.SetSymCustomAttribute(name, data);
    }
    #endregion

    #region Inner implementations
    #region Plain Method define
    protected virtual MethodBuilder WrapDefinePlainMethod(string name, MethodAttributes attributes, Type returnType, Type[] parameterTypes)
    {
      return this.Builder.DefineGlobalMethod(name, attributes, returnType, parameterTypes);
    }
    protected virtual MethodBuilder WrapDefinePlainMethod(string name, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes)
    {
      return this.Builder.DefineGlobalMethod(name, attributes, callingConvention, returnType, parameterTypes);
    }
    protected virtual MethodBuilder WrapDefinePInvokeMethod(string name, string dllName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet)
    {
      return this.Builder.DefinePInvokeMethod(
        name,
        dllName,
        attributes,
        callingConvention,
        returnType,
        parameterTypes,
        nativeCallConv,
        nativeCharSet
        );
    }
    protected virtual MethodBuilder WrapDefinePInvokeMethod(string name, string dllName, string entryName, MethodAttributes attributes, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, CallingConvention nativeCallConv, CharSet nativeCharSet)
    {
      return this.Builder.DefinePInvokeMethod(
        name,
        dllName,
        entryName,
        attributes,
        callingConvention,
        returnType,
        parameterTypes,
        nativeCallConv,
        nativeCharSet
        );
    }
    #endregion
    #region Static Field define
    protected virtual FieldBuilder WrapDefineInitializedData(string name, byte[] data, FieldAttributes attributes)
    {
      return this.Builder.DefineInitializedData(name, data, attributes);
    }
    protected virtual FieldBuilder WrapDefineUninitializedData(string name, int size, FieldAttributes attributes)
    {
      return this.Builder.DefineUninitializedData(name, size, attributes);
    }
    #endregion
    #region Type define
    protected virtual EnumBuilder WrapDefineEnum(string name, TypeAttributes attributes, Type underlingType)
    {
      return this.Builder.DefineEnum(name, attributes, underlingType);
    }
    protected virtual TypeBuilder WrapDefineType(string name)
    {
      return this.Builder.DefineType(name);
    }
    protected virtual TypeBuilder WrapDefineType(string name, TypeAttributes attr)
    {
      return this.Builder.DefineType(name, attr);
    }
    protected virtual TypeBuilder WrapDefineType(string name, TypeAttributes attr, Type parent)
    {
      return this.Builder.DefineType(name, attr, parent);
    }
    protected virtual TypeBuilder WrapDefineType(string name, TypeAttributes attr, Type parent, PackingSize packSize)
    {
      return this.Builder.DefineType(name, attr, parent, packSize);
    }
    protected virtual TypeBuilder WrapDefineType(string name, TypeAttributes attr, Type parent, Type[] interfaces)
    {
      return this.Builder.DefineType(name, attr, parent, interfaces);
    }
    #endregion
    #region Entry point
    protected virtual void SetUserEntryPoint(MethodInfo method)
    {
      this.Builder.SetUserEntryPoint(method);
    }
    #endregion
    #endregion
    #region Utils
    protected virtual FieldGenerator AddField(FieldBuilder field)
    {
      return this.AddField(FieldGenerator.of(field));
    }
    protected virtual MethodGenerator AddMethod(MethodBuilder builder)
    {
      return this.AddMethod(MethodGenerator.of(builder));
    }
    protected virtual TypeGenerator AddType(TypeBuilder builder)
    {
      return this.AddType(TypeGenerator.of(builder));
    }
    protected virtual TypeGenerator AddType(EnumBuilder builder)
    {
      return this.AddType(EnumGenerator.of(builder));
    }
    protected virtual MethodGenerator AddMethod(MethodGenerator g)
    {
      this.Methods.Add(g);
      return g;
    }
    protected virtual TypeGenerator AddType(TypeGenerator g)
    {
      this.Types.Add(g);
      return g;
    }
    protected virtual FieldGenerator AddField(FieldGenerator g)
    {
      this.Fields.Add(g);
      return g;
    }
    #endregion
    #endregion
    #endregion
    #region Utils
    protected override void Dispose(bool disposing)
    {
      if (disposing)
      {
        this.builder = null;
      }
      base.Dispose(disposing);
    }
    protected override void DisposeCollection(bool disposing)
    {
      if (disposing)
      {
        this.DisposeCollection(this.types);
        this.DisposeCollection(this.plainMethods);
      }
      base.DisposeCollection(disposing);
    }
    #endregion
  }
}
